/************************************************************************
* events_w32.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#define _WM_EXPOSE_ALL
#include "wm.h"

#ifdef WIN32

#include <wctype.h>

int w32_key_to_sym(sym_t *s, uint32_t key)
{
	uint32_t wc;
	uint16_t str[2];

	switch (key) {
	case VK_LSHIFT:
	case VK_RSHIFT:
	case VK_SHIFT:
		s->type = SYM_TYPE_MOD;
		s->sym = SYM_MOD_SHIFT;
		break;
	case VK_LCONTROL:
	case VK_RCONTROL:
		s->type = SYM_TYPE_MOD;
		s->sym = SYM_MOD_CTRL;
		break;
	case VK_MENU: /* alt */
		s->type = SYM_TYPE_MOD;
		s->sym = SYM_MOD_ALT;
		break;
	case VK_LWIN:
	case VK_RWIN:
		s->type = SYM_TYPE_MOD;
		s->sym = SYM_MOD_SUPER;
		break;
	case VK_SPACE:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_SPACE;
		break;
	case VK_ESCAPE:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_ESCAPE;
		break;
	case VK_TAB:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_TAB;
		break;
	case VK_NUMPAD0:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP0;
		break;
	case VK_NUMPAD1:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP1;
		break;
	case VK_NUMPAD2:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP2;
		break;
	case VK_NUMPAD3:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP3;
		break;
	case VK_NUMPAD4:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP4;
		break;
	case VK_NUMPAD5:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP5;
		break;
	case VK_NUMPAD6:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP6;
		break;
	case VK_NUMPAD7:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP7;
		break;
	case VK_NUMPAD8:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP8;
		break;
	case VK_NUMPAD9:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KP9;
		break;
	case VK_LEFT:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_LEFT;
		break;
	case VK_UP:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_UP;
		break;
	case VK_RIGHT:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_RIGHT;
		break;
	case VK_DOWN:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_DOWN;
		break;
	case VK_F1:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F1;
		break;
	case VK_F2:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F2;
		break;
	case VK_F3:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F3;
		break;
	case VK_F4:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F4;
		break;
	case VK_F5:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F5;
		break;
	case VK_F6:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F6;
		break;
	case VK_F7:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F7;
		break;
	case VK_F8:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F8;
		break;
	case VK_F9:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F9;
		break;
	case VK_F10:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F10;
		break;
	case VK_F11:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F11;
		break;
	case VK_F12:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_F12;
		break;
	case VK_BACK:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_BCKSPC;
		break;
	case VK_RETURN:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_RETURN;
		break;
	case VK_DELETE:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_DELETE;
		break;
	case VK_MULTIPLY:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KPMUL;
		break;
	case VK_ADD:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KPADD;
		break;
	case VK_SUBTRACT:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KPSUB;
		break;
	case VK_DECIMAL:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KPDOT;
		break;
	case VK_DIVIDE:
		s->type = SYM_TYPE_SKEY;
		s->sym = SYM_KEY_KPDIV;
		break;
	case VK_CLEAR:
	case VK_PAUSE:
	case VK_SCROLL:
	case VK_HOME:
	case VK_PRIOR:
	case VK_NEXT:
	case VK_END:
	case VK_SELECT:
	case VK_PRINT:
	case VK_EXECUTE:
	case VK_INSERT:
	case VK_CANCEL:
	case VK_HELP:
	case VK_NUMLOCK:
	case VK_CAPITAL:
		break;
	default:
		wc = MapVirtualKey(key,2);
		if (wc) {
			str[0] = LOWORD(wc);
			str[1] = 0;
			s->sym = utf16_toutf32(str);
			if (!iswgraph(s->sym))
				s->sym = 0;
		}
	}


	return 0;
}

/* oh god w32, wtf */
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	event_t e;
	uint16_t buff[5];
	BYTE ks[256];
	e.type = EVENT_NONE;
	e.sym.type = SYM_TYPE_NONE;
	e.sym.sym = 0;
	e.sym.ch = 0;

	switch (uMsg) {
	case WM_ACTIVATE:
		return 0;
	case WM_SYSCOMMAND:
	{
		switch (wParam) {
		case SC_SCREENSAVE:
		case SC_MONITORPOWER:
			return 0;
		}
		break;
	}
	case WM_CLOSE:
		PostQuitMessage(0);
		return 0;
	case WM_KEYDOWN:
	{
		/* this should put the character created by the key into e.sym.ch,
		 * and either the unmodified (ignore shift, etc) character, or special value into e.sym.sym */
		e.type = EVENT_KEY_DOWN;
		GetKeyboardState(ks);
		if (ToUnicode(wParam, MapVirtualKey(wParam,0), ks, (LPWSTR)buff, 5, 0) > 0)
			e.ch = utf16_toutf32(buff);
		w32_key_to_sym(&e.sym,wParam);
		if (e.sym.sym)
			events_handle(&e);
		return 0;
	}
	case WM_KEYUP:
	{
		/* this should put the unmodified (ignore shift, etc) character, or special value into e.sym.sym */
		e.type = EVENT_KEY_UP;
		w32_key_to_sym(&e.sym,wParam);
		if (e.sym.sym)
			events_handle(&e);
		return 0;
	}
	case WM_SIZE:
	{
		wm_data.size.width = LOWORD(lParam);
		wm_data.size.height = HIWORD(lParam);
		return 0;
	}
	case WM_LBUTTONDOWN:
	{
		e.type = EVENT_BUTTON_DOWN;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_LEFT;
		events_handle(&e);
		return 0;
	}
	case WM_LBUTTONUP:
	{
		e.type = EVENT_BUTTON_UP;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_LEFT;
		events_handle(&e);
		return 0;
	}
	case WM_MBUTTONDOWN:
	{
		e.type = EVENT_BUTTON_DOWN;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_CENTRE;
		events_handle(&e);
		return 0;
	}
	case WM_MBUTTONUP:
	{
		e.type = EVENT_BUTTON_UP;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_CENTRE;
		events_handle(&e);
		return 0;
	}
	case WM_RBUTTONDOWN:
	{
		e.type = EVENT_BUTTON_DOWN;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_RIGHT;
		events_handle(&e);
		return 0;
	}
	case WM_RBUTTONUP:
	{
		e.type = EVENT_BUTTON_UP;
		e.sym.type = SYM_TYPE_MOUSE;
		e.sym.sym = MOUSE_BUTTON_RIGHT;
		events_handle(&e);
		return 0;
	}
	case WM_MOUSEMOVE:
	{
		e.type = EVENT_MOUSE_MOTION;
		e.x = LOWORD(lParam);
		e.y =  HIWORD(lParam);
		events_handle(&e);
		return 0;
	}
	case WM_MOUSEWHEEL:
	{
		e.type = EVENT_BUTTON_DOWN;
		e.sym.type = SYM_TYPE_MOUSE;
		if (wParam == VK_XBUTTON1) {
			e.sym.sym = MOUSE_BUTTON_UP;
			events_handle(&e);
		}else if (wParam == VK_XBUTTON2) {
			e.sym.sym = MOUSE_BUTTON_DOWN;
			events_handle(&e);
		}
		return 0;
	}
	}

	/* pass unhandled messages to DefWindowProc */
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}


/* read and act on events */
void events_main()
{
	MSG msg;
	while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
		if (msg.message == WM_QUIT) {
			client_state(TUSTATE_EXIT);
		}else{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	events_trigger_active();
}
#endif
